use std::fmt::Display;

pub trait JSign: Display + PartialEq {}

#[derive(PartialEq)]
pub struct JSignVoid;
impl JSign for JSignVoid {}
impl Display for JSignVoid {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.write_str("V")
	}
}
#[derive(PartialEq)]
pub struct JSignBool;
impl JSign for JSignBool {}
impl Display for JSignBool {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.write_str("Z")
	}
}
#[derive(PartialEq)]
pub struct JSignByte;
impl JSign for JSignByte {}
impl Display for JSignByte {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.write_str("B")
	}
}
#[derive(PartialEq)]
pub struct JSignChar;
impl JSign for JSignChar {}
impl Display for JSignChar {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.write_str("C")
	}
}
#[derive(PartialEq)]
pub struct JSignShort;
impl JSign for JSignShort {}
impl Display for JSignShort {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.write_str("S")
	}
}
#[derive(PartialEq)]
pub struct JSignInt;
impl JSign for JSignInt {}
impl Display for JSignInt {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.write_str("I")
	}
}
#[derive(PartialEq)]
pub struct JSignLong;
impl JSign for JSignLong {}
impl Display for JSignLong {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.write_str("J")
	}
}
#[derive(PartialEq)]
pub struct JSignFloat;
impl JSign for JSignFloat {}
impl Display for JSignFloat {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.write_str("F")
	}
}
#[derive(PartialEq)]
pub struct JSignDouble;
impl JSign for JSignDouble {}
impl Display for JSignDouble {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.write_str("D")
	}
}
#[derive(PartialEq)]
pub struct JSignRef<T: AsRef<str> + PartialEq + Display + 'static>(pub T);
impl<T: AsRef<str> + PartialEq + Display + 'static> JSign for JSignRef<T> {}
impl<T: AsRef<str> + PartialEq + Display + 'static> Display for JSignRef<T> {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.write_str("L")?;
		self.0.fmt(f)?;
		f.write_str(";")
	}
}
#[derive(PartialEq)]
pub struct JSignArray<Item: JSign>(pub Item);
impl<Item: JSign> JSign for JSignArray<Item> {}
impl<Item: JSign> Display for JSignArray<Item> {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.write_str("[")?;
		self.0.fmt(f)
	}
}
#[derive(PartialEq)]
pub struct JSignFun<Param: JSignArg, Ret: JSign>(pub Param, pub Ret);
impl<Param: JSignArg, Ret: JSign> JSign for JSignFun<Param, Ret> {}
impl<Param: JSignArg, Ret: JSign> Display for JSignFun<Param, Ret> {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.write_str("(")?;
		self.0.fmt(f)?;
		f.write_str(")")?;
		self.1.fmt(f)
	}
}

pub trait JSignArg: JSign {}

#[derive(PartialEq)]
pub struct JSignArg0;
impl JSign for JSignArg0 {}
impl JSignArg for JSignArg0 {}
impl Display for JSignArg0 {
	fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		Ok(())
	}
}

macro_rules! tuple_impl_serial {
	($cls:ident $($n:tt $name:ident)*) => {
		#[derive(PartialEq)]
		pub struct $cls<$($name:JSign,)*>($(pub $name,)*);
		impl<$($name:JSign,)*> JSign for $cls<$($name,)*> {}
		impl<$($name:JSign,)*> JSignArg for $cls<$($name,)*> {}
		impl<$($name:JSign,)*> Display for $cls<$($name,)*> {
			fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
				$(self.$n.fmt(f)?;)*
				Ok(())
			}
		}
	};
}

tuple_impl_serial!(JSignArg1 0 A);
tuple_impl_serial!(JSignArg2 0 A 1 B);
tuple_impl_serial!(JSignArg3 0 A 1 B 2 C);
tuple_impl_serial!(JSignArg4 0 A 1 B 2 C 3 D);
tuple_impl_serial!(JSignArg5 0 A 1 B 2 C 3 D 4 E);
tuple_impl_serial!(JSignArg6 0 A 1 B 2 C 3 D 4 E 5 F);
tuple_impl_serial!(JSignArg7 0 A 1 B 2 C 3 D 4 E 5 F 6 G);
tuple_impl_serial!(JSignArg8 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H);
tuple_impl_serial!(JSignArg9 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I);
tuple_impl_serial!(JSignArg10 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J);
tuple_impl_serial!(JSignArg11 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K);
tuple_impl_serial!(JSignArg12 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L);
tuple_impl_serial!(JSignArg13 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L 12 M);
tuple_impl_serial!(JSignArg14 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L 12 M 13 N);
tuple_impl_serial!(JSignArg15 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L 12 M 13 N 14 O);
tuple_impl_serial!(JSignArg16 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L 12 M 13 N 14 O 15 P);
tuple_impl_serial!(JSignArg17 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L 12 M 13 N 14 O 15 P 16 Q);
tuple_impl_serial!(JSignArg18 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L 12 M 13 N 14 O 15 P 16 Q 17 R);
tuple_impl_serial!(JSignArg19 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L 12 M 13 N 14 O 15 P 16 Q 17 R 18 S);
tuple_impl_serial!(JSignArg20 0 A 1 B 2 C 3 D 4 E 5 F 6 G 7 H 8 I 9 J 10 K 11 L 12 M 13 N 14 O 15 P 16 Q 17 R 18 S 19 T);

#[test]
fn test_sign_fun() {
	let sign = JSignFun(JSignArg0, JSignVoid);
	assert_eq!("()V", format!("{sign}"));
	let sign = JSignFun(JSignArg1(JSignInt), JSignLong);
	assert_eq!("(I)J", format!("{sign}"));
	let sign = JSignFun(JSignArg3(JSignInt, JSignRef("java/lang/String"), JSignArray(JSignInt)), JSignLong);
	assert_eq!("(ILjava/lang/String;[I)J", format!("{sign}"));
	println!("{sign}");
}
